
global bandas_asm

section .rodata
MASCARA_FILTRO: DW 0XFFFF,0,0,0,0XFFFF,0,0,0 ; mascara para filtrar las sumas de los valores de los pixeles
MASCARA_INVERTIR: DW 0XFFFF,0XFFFF,0XFFFF,0,0XFFFF,0XFFFF,0XFFFF,0 ; mascara para invertir los bits

BOUND_1: DW  96,  96,  96, 0,  96,  96,  96, 0
BOUND_2: DW 288, 288, 288, 0, 288, 288, 288, 0
BOUND_3: DW 480, 480, 480, 0, 480, 480, 480, 0
BOUND_4: DW 672, 672, 672, 0, 672, 672, 672, 0

COLORES_2: DW  64,  64,  64, 0,  64,  64,  64, 0
COLORES_3: DW 128, 128, 128, 0, 128, 128, 128, 0
COLORES_4: DW 192, 192, 192, 0, 192, 192, 192, 0
COLORES_5: DW 255, 255, 255, 0, 255, 255, 255, 0

section .data

section .text
;void bandas_asm    (
	;unsigned char *src,
	;unsigned char *dst,
	;int filas,
	;int cols,
	;int src_row_size,
	;int dst_row_size)

; Parametros:
; RDI=*src
; RSI=*dst
; EDX=cols
; ECX=filas 
; R8D=src_row_size 
; R9D=dst_row_size
bandas_asm:
; Guardamos los registros de la convención C
	; (pila desalineada)
	PUSH RBP ; (pila alineada)
	MOV RBP, RSP
	PUSH RBX ; (pila desalineada)
	PUSH R12 ; (pila alineada)
	PUSH R13 ; (pila desalineada)
	PUSH R14 ; (pila alineada)
	PUSH R15 ; (pila desalineada)

	; Esto es para alinear la pila (por el call que voy a usar)
	sub rsp,8

	; Reseteamos los contadores para los ciclos
	XOR R12, R12 ; Contador para el ciclo 'filas' (alto)
	XOR R13, R13 ; Contador para el ciclo 'cols' (ancho)
	XOR RBX, RBX

	pxor xmm0, xmm0
	pxor xmm1, xmm1				;uso el xmm1 para desempaquetar
	movdqu xmm2, [MASCARA_FILTRO]

	movdqu XMM4, [BOUND_1]
	movdqu XMM5, [BOUND_2]
	movdqu XMM6, [BOUND_3]	
	movdqu XMM11, [BOUND_4]

	movdqu xmm10, [MASCARA_INVERTIR]
	movdqu xmm12, [COLORES_2]
	movdqu xmm13, [COLORES_3]
	movdqu xmm14, [COLORES_4]
	movdqu xmm15, [COLORES_5]

.ciclo_h:

  CMP R12, RCX
  JE .salir ; si r12 = rcx = filas, termino el ciclo

	.ciclo_w:
		CMP R13, RDX
		JGE .siguienteFila ; si r13 >= rdx (= cols), me pase del ancho de la imagen o estoy en el limite

		;--------------------------------------------------------------------
		; Determinamos si quedan 3 o menos pixeles por procesar para la fila
		; actual. Si no es así (es decir, si quedan 4 o más) el ciclo puede
		; continuar normalmente.
		;--------------------------------------------------------------------
		MOV RAX, rdx
		SUB RAX, R13
		CMP RAX, 3
		JG .quedan_mas_de_4_pixeles

		;-----------------------------------------------------------------------
		; Llegar a este punto significa que quedan entre 1 y 3 pixeles finales
		; en la fila actual. Dado que procesamos de a 4 pixeles simultáneamente,
		; habrá que re-ajustar la próxima dirección retrocediendo el número de
		; bytes adecuado para meter esos últimos 4 pixeles en el registro XMM
		;-----------------------------------------------------------------------
		MOV R11, 4
		SUB R11, RAX  ; R11 almacena el número de pixeles que hay que retroceder.
		SUB R13, R11
		SUB RBX, R11
		SUB RBX, R11
		SUB RBX, R11  ; retrocedo 3 veces la cantidad de pixeles que hay que retroceder

    	.quedan_mas_de_4_pixeles:

			movdqu XMM0, [RDI+RBX]	; XMM0 = |A4|R4|G4|B4|A3|R3|G3|B3|A2|R2|G2|B2|A1|R1|G1|B1|
			movdqu XMM7, XMM0		; XMM7 = |A4|R4|G4|B4|A3|R3|G3|B3|A2|R2|G2|B2|A1|R1|G1|B1|

			;-------------------------------------------------
			; 1) PROCESO LOS PRIMEROS 2 PIXELS (2 grupos de 2)
			;-------------------------------------------------
			punpcklbw XMM0, XMM1	;paso de byte a word para no perder precision al sumar
								; XMM0 = |  A2  |  R2  |  G2  |  B2  |  A1  |  R1  |  G1  |  B1  |

			movdqu xmm3, xmm0	; XMM3 = |  A2  |  R2  |  G2  |  B2  |  A1  |  R1  |  G1  |  B1  |
			psrldq xmm3, 2		; XMM3 = |   0  |  A2  |  R2  |  G2  |  B2  |  A1  |  R1  |  G1  |

			movdqu xmm8, xmm0	; XMM8 = |  A2  |  R2  |  G2  |  B2  |  A1  |  R1  |  G1  |  B1  |
			psrldq xmm8, 4		; XMM8 = |   0  |   0  |  A2  |  R2  |  G2  |  B2  |  A1  |  R1  |

			paddw xmm0, xmm3 	; xmm0 = |...|...|...|B2+G2|...|...|...|B1+G1|
			paddw xmm0, xmm8 	; xmm0 = |...|...|...|B2+G2+R2|...|...|...|B1+G1+R1|
			pand xmm0, xmm2	 	; xmm0 = | 0 | 0 | 0 |B2+G2+R2| 0 | 0 | 0 |B1+G1+R1|

			movdqu xmm3, xmm0	; xmm3 = | 0 | 0 | 0 |suma2| 0 | 0 | 0 |suma1|
			movdqu xmm8, xmm0	; xmm8 = | 0 | 0 | 0 |suma2| 0 | 0 | 0 |suma1|

			pslldq xmm3, 2		; XMM3 = | 0 |  0  |suma2| 0 | 0 |  0  |suma1| 0 |
			pslldq xmm8, 4		; XMM8 = | 0 |suma2| 0   | 0 | 0 |suma1|  0  | 0 |

			paddw xmm0, xmm3	; xmm0 = | 0 |  0  |suma2|suma2| 0 |  0  |suma1|suma1|
			paddw xmm0, xmm8	; xmm0 = | 0 |suma2|suma2|suma2| 0 |suma1|suma1|suma1|

			movdqu xmm3, xmm0

			; Ahora tenemos los datos acomodados, 
			; hay que hacer los compare y hacerles or entre si para ir mezclando los resultados

			;;;;;;;;;;;;;;
			; 1.1) Banda 1
			;;;;;;;;;;;;;;
			pxor xmm9, xmm9
			movdqu xmm9, xmm4 ; xmm9 = |0|0|96|96|0|96|96|96| - [BOUND_1]
			pcmpgtw xmm9, xmm0	; xmm9  =   |0|(SR2<96) ? FFFF : 0|(SG2<96) ? FFFF : 0|(SB2<96) ? FFFF : 0|
								;			|0|(SR1<96) ? FFFF : 0|(SG1<96) ? FFFF : 0|(SB1<96) ? FFFF : 0|

			; dejamos todo en 0 porque en esta banda siempre todos son ceros
			pxor xmm0, xmm0		; xmm0 = |0|0|0|0|0|0|0|0|

			;;;;;;;;;;;;;;
			; 1.2) Banda 2
			;;;;;;;;;;;;;;
			pxor xmm9, xmm10 ; Invierto los bits de xmm9 para quedarme con los que son >= 96
			pxor xmm8, xmm8
			movdqu xmm8, xmm5 ; xmm8 = |0|288|288|288|0|288|288|288| - [BOUND_2]
			pcmpgtw xmm8, xmm3	; xmm8  =   |0|(SR2<288) ? FFFF : 0|(SG2<288) ? FFFF : 0|(SB2<288) ? FFFF : 0|
								;			|0|(SR1<288) ? FFFF : 0|(SG1<288) ? FFFF : 0|(SB1<288) ? FFFF : 0|

			; me fijo si estan en la Banda 2 => 96<=suma<288
			pand xmm9, xmm8	; xmm9  =   |0|(96<=SR2<288) ? FFFF : 0|(96<=SG2<288) ? FFFF : 0|(96<=SB2<288) ? FFFF : 0|
							;			|0|(96<=SR1<288) ? FFFF : 0|(96<=SG1<288) ? FFFF : 0|(96<=SB1<1288) ? FFFF : 0|

			pand xmm9, xmm12	; xmm9 = |0|64 o 0|64 o 0|64 o 0|0|64 o 0|64 o 0|64 o 0|
			movdqu xmm0, xmm9 ; obtenemos el resultado intermedio entre cruzar las bandas 1 y 2

			;;;;;;;;;;;;;;
			; 1.3) Banda 3
			;;;;;;;;;;;;;;
			pxor xmm8, xmm10 ; Invierto los bits de xmm8 para quedarme con los que son >= 288
			pxor xmm9, xmm9
			movdqu xmm9, xmm6 ; xmm9 = |0|480|480|480|0|480|480|480| - [BOUND_3]
			pcmpgtw xmm9, xmm3	; xmm9  =   |0|(SR2<480) ? FFFF : 0|(SG2<480) ? FFFF : 0|(SB2<480) ? FFFF : 0|
								;			|0|(SR1<480) ? FFFF : 0|(SG1<480) ? FFFF : 0|(SB1<480) ? FFFF : 0|

			; me fijo si estan en la Banda 3 => 288<=suma<480
			pand xmm8, xmm9	; xmm8  =   |0|(288<=SR2<480) ? FFFF : 0|(288<=SG2<480) ? FFFF : 0|(288<=SB2<480) ? FFFF : 0|
							;			|0|(288<=SR1<480) ? FFFF : 0|(288<=SG1<480) ? FFFF : 0|(288<=SB1<480) ? FFFF : 0|

			pand xmm8, xmm13	; xmm8 = |0|128 o 0|128 o 0|128 o 0|0|128 o 0|128 o 0|128 o 0|
			pxor xmm0, xmm8 ; obtenemos el resultado intermedio entre cruzar las bandas 1, 2 y 3

			;;;;;;;;;;;;;;
			; 1.4) Banda 4
			;;;;;;;;;;;;;;
			pxor xmm9, xmm10 ; Invierto los bits de xmm9 para quedarme con los que son >= 480
			pxor xmm8, xmm8
			movdqu xmm8, xmm11 ; xmm8 = |0|672|672|672|0|672|672|672| - [BOUND_4]
			pcmpgtw xmm8, xmm3	; xmm8  =   |0|(SR2<672) ? FFFF : 0|(SG2<672) ? FFFF : 0|(SB2<672) ? FFFF : 0|
								;			|0|(SR1<672) ? FFFF : 0|(SG1<672) ? FFFF : 0|(SB1<672) ? FFFF : 0|

			; me fijo si estan en la Banda 4 => 480<=suma<672
			pand xmm9, xmm8	; xmm9  =  |0|0|(288<=SR2<480) ? FFFF : 0|(288<=SG2<480) ? FFFF : 0|(288<=SB2<480) ? FFFF : 0|
							;			|(288<=SR1<480) ? FFFF : 0|(288<=SG1<480) ? FFFF : 0|(288<=SB1<480) ? FFFF : 0|

			pand xmm9, xmm14	; xmm9 = |0|192 o 0|192 o 0|192 o 0|0|192 o 0|192 o 0|192 o 0|
			pxor xmm0, xmm9 ; obtenemos el resultado intermedio entre cruzar las bandas 1, 2, 3 y 4

			;;;;;;;;;;;;;;
			; 1.5) Banda 5
			;;;;;;;;;;;;;;
			pxor xmm8, xmm10 ; Invierto los bits de xmm8 para quedarme con los que son >= 672
			pand xmm8, xmm15	; xmm8 = |0|255 o 0|255 o 0|255 o 0|0|255 o 0|255 o 0|255 o 0|

			pxor xmm0, xmm8		; xmm0 = |0|P2R|P2G|P2B|0|P1R|P1G|P1B|


			;------------------------------------------------
			; 2) PROCESO LOS ULTIMOS 2 PIXELS (2 grupos de 2)
			;------------------------------------------------
			punpckhbw XMM7, XMM1	;paso de byte a word para no perder precision al sumar
								; XMM7 = |  A4  |  R4  |  G4  |  B4  |  A3  |  R3  |  G3  |  B3  |

			movdqu xmm3, xmm7	; XMM3 = |  A4  |  R4  |  G4  |  B4  |  A3  |  R3  |  G3  |  B3  |
			psrldq xmm3, 2		; XMM3 = |   0  |  A4  |  R4  |  G4  |  B4  |  A3  |  R3  |  G3  |

			movdqu xmm8, xmm7	; XMM8 = |  A4  |  R4  |  G4  |  B4  |  A3  |  R3  |  G3  |  B3  |
			psrldq xmm8, 4		; XMM8 = |   0  |   0  |  A4  |  R4  |  G4  |  B4  |  A3  |  R3  |

			paddw xmm7, xmm3 	; xmm7 = |...|...|...|B4+G4|...|...|...|B3+G3|
			paddw xmm7, xmm8 	; xmm7 = |...|...|...|B4+G4+R4|...|...|...|B3+G3+R3|
			pand xmm7, xmm2	 	; xmm7 = | 0 | 0 | 0 |B4+G4+R4| 0 | 0 | 0 |B3+G3+R3|

			movdqu xmm3, xmm7	; xmm3 = | 0 | 0 | 0 |suma4| 0 | 0 | 0 |suma3|
			movdqu xmm8, xmm7	; xmm8 = | 0 | 0 | 0 |suma4| 0 | 0 | 0 |suma3|

			pslldq xmm3, 2		; XMM3 = | 0 |  0  |suma4|  0  | 0 |  0  |suma3| 0 |
			pslldq xmm8, 4		; XMM8 = | 0 |suma4|  0  |  0  | 0 |suma3|  0  | 0 |

			paddw xmm7, xmm3	; xmm7 = | 0 |  0  |suma4|suma4| 0 |  0  |suma3|suma3|
			paddw xmm7, xmm8	; xmm7 = | 0 |suma4|suma4|suma4| 0 |suma3|suma3|suma3|

			movdqu xmm3, xmm7

			; Ahora tenemos los datos acomodados, 
			; hay que hacer los compare y hacerles or entre si para ir mezclando los resultados
			;;;;;;;;;;;;;;
			; 2.1) Banda 1
			;;;;;;;;;;;;;;
			pxor xmm9, xmm9
			movdqu xmm9, xmm4 ; xmm9 = |0|96|96|96|0|96|96|96| - [BOUND_1]
			pcmpgtw xmm9, xmm7	; xmm9  =   |0|(SR4<96) ? FFFF : 0|(SG4<96) ? FFFF : 0|(SB4<96) ? FFFF : 0|
								;			|0|(SR3<96) ? FFFF : 0|(SG3<96) ? FFFF : 0|(SB3<96) ? FFFF : 0|

			; dejamos todo en 0 porque en esta banda siempre todos son ceros
			pxor xmm7, xmm7		; xmm7 = |0|0|0|0|0|0|0|0|

			;;;;;;;;;;;;;;
			; 2.2) Banda 2
			;;;;;;;;;;;;;;
			pxor xmm9, xmm10 ; Invierto los bits de xmm9 para quedarme con los que son >= 96
			pxor xmm8, xmm8
			movdqu xmm8, xmm5 ; xmm8 = |0|288|288|288|0|288|288|288| - [BOUND_2]
			pcmpgtw xmm8, xmm3	; xmm8  =   |0|(SR4<288) ? FFFF : 0|(SG4<288) ? FFFF : 0|(SB4<288) ? FFFF : 0|
								;			|0|(SR3<288) ? FFFF : 0|(SG3<288) ? FFFF : 0|(SB3<288) ? FFFF : 0|

			; me fijo si estan en la Banda 2 => 96<=suma<288
			pand xmm9, xmm8	; xmm9  =   |0|(96<=SR4<288) ? FFFF : 0|(96<=SG4<288) ? FFFF : 0|(96<=SB4<288) ? FFFF : 0|
							;			|0|(96<=SR3<288) ? FFFF : 0|(96<=SG3<288) ? FFFF : 0|(96<=SB3<1288) ? FFFF : 0|
								
			pand xmm9, xmm12	; xmm9 = |0|64 o 0|64 o 0|64 o 0|0|64 o 0|64 o 0|64 o 0|
			movdqu xmm7, xmm9 ; obtenemos el resultado intermedio entre cruzar las bandas 1 y 2

			;;;;;;;;;;;;;;
			; 2.3) Banda 3
			;;;;;;;;;;;;;;
			pxor xmm8, xmm10 ; Invierto los bits de xmm8 para quedarme con los que son >= 288
			pxor xmm9, xmm9
			movdqu xmm9, xmm6 ; xmm9 = |0|480|480|480|0|480|480|480| - [BOUND_3]
			pcmpgtw xmm9, xmm3	; xmm9  =   |0|(SR4<480) ? FFFF : 0|(SG4<480) ? FFFF : 0|(SB4<480) ? FFFF : 0|
								;			|0|(SR3<480) ? FFFF : 0|(SG3<480) ? FFFF : 0|(SB3<480) ? FFFF : 0|

			; me fijo si estan en la Banda 3 => 288<=suma<480
			pand xmm8, xmm9	; xmm8  =   |0|(288<=SR4<480) ? FFFF : 0|(288<=SG4<480) ? FFFF : 0|(288<=SB4<480) ? FFFF : 0|
							;			|0|(288<=SR3<480) ? FFFF : 0|(288<=SG3<480) ? FFFF : 0|(288<=SB3<480) ? FFFF : 0|

			pand xmm8, xmm13	; xmm8 = |0|128 o 0|128 o 0|128 o 0|0|128 o 0|128 o 0|128 o 0|
			pxor xmm7, xmm8 ; obtenemos el resultado intermedio entre cruzar las bandas 1, 2 y 3

			;;;;;;;;;;;;;;
			; 2.4) Banda 4
			;;;;;;;;;;;;;;
			pxor xmm9, xmm10 ; Invierto los bits de xmm9 para quedarme con los que son >= 480
			pxor xmm8, xmm8
			movdqu xmm8, xmm11 ; xmm8 = |0|672|672|672|0|672|672|672| - [BOUND_4]
			pcmpgtw xmm8, xmm3	; xmm8  =   |0|(SR4<672) ? FFFF : 0|(SG4<672) ? FFFF : 0|(SB4<672) ? FFFF : 0|
								;			|0|(SR3<672) ? FFFF : 0|(SG3<672) ? FFFF : 0|(SB3<672) ? FFFF : 0|

			; me fijo si estan en la Banda 4 => 480<=suma<672
			pand xmm9, xmm8	; xmm9  =   |0|(288<=SR4<480) ? FFFF : 0|(288<=SG4<480) ? FFFF : 0|(288<=SB4<480) ? FFFF : 0|
							;			|0|(288<=SR3<480) ? FFFF : 0|(288<=SG3<480) ? FFFF : 0|(288<=SB3<480) ? FFFF : 0|

			pand xmm9, xmm14	; xmm9 = |0|192 o 0|192 o 0|192 o 0|0|192 o 0|192 o 0|192 o 0|
			pxor xmm7, xmm9 ; obtenemos el resultado intermedio entre cruzar las bandas 1, 2, 3 y 4

			;;;;;;;;;;;;;;
			; 2.5) Banda 5
			;;;;;;;;;;;;;;
			pxor xmm8, xmm10 ; Invierto los bits de xmm8 para quedarme con los que son >= 672
			pand xmm8, xmm15	; xmm8 = |0|255 o 0|255 o 0|255 o 0|0|255 o 0|255 o 0|255 o 0|

			pxor xmm7, xmm8		; xmm7 = |0|P4R|P4G|P4B|0|P3R|P3G|P3B|


			; EMPAQUETAMOS LOS DATOS
			packuswb xmm0, xmm1 ; xmm0 = |0|0|0|0|0|0|0|0|0|P2R|P2G|P2B|0|P1R|P1G|P1B|
			packuswb xmm7, xmm1 ; xmm7 = |0|0|0|0|0|0|0|0|0|P4R|P4G|P4B|0|P3R|P3G|P3B|
			pslldq xmm7, 8		; xmm7 = |0|P4R|P4G|P4B|0|P3R|P3G|P3B|0|0|0|0|0|0|0|0|
			pxor xmm0, xmm7		; xmm0 = |0|P4R|P4G|P4B|0|P3R|P3G|P3B|0|P2R|P2G|P2B|0|P1R|P1G|P1B|


			; Ahora tengo que pasarle los 4 pixeles (16 bytes a la imagen destino)
			MOVDQU [RSI+RBX], XMM0

.continuar_w:

	ADD R13, 4	; Avanzo hasta los proximos 4 pixels de la imagen dst
	ADD RBX, 16	; Avanzo hasta los proximos 4 pixels (4*4=16 bytes) de la imagen src

JMP .ciclo_w


.siguienteFila:

	XOR R13, R13	; Reseteo el contador de ancho
	XOR RBX, RBX 	; Puntero para la fila actual (en la que estan los pixeles a escribir)

	ADD RDI, R8 	; paso a la siguiente fila de la imagen src
	ADD RSI, R9 	; paso a la siguiente fila de la imagen dst
	INC R12			; aumento una fila, para pasar a la siguiente

jmp .ciclo_h


.salir:
	add rsp,8

	; Restauramos los registros de la convención C.
	POP R15
	POP R14
	POP R13
	POP R12
	POP RBX
	POP RBP
    ret
 
